package app

import (
	"context"
	url2 "net/url"
	"strconv"
	"strings"

	"github.com/glennliao/apijson-go"
	"github.com/glennliao/apijson-go/config"
	"github.com/glennliao/apijson-go/model"
	"github.com/glennliao/bookmark/app/util/fetchurl"
	"github.com/glennliao/bookmark/app/util/htmlbookmark"
	"github.com/gogf/gf/v2/container/gset"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/gclient"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/panjf2000/ants/v2"
	"github.com/yitter/idgenerator-go/idgen"
)

func 初始化函数(api实例 *apijson.APIJSON) {
	api实例.H获取配置().H函数集合.B绑定函数("fetchURL", 获取URL信息)
	api实例.H获取配置().H函数集合.B绑定函数("import", 导入书签)

	api实例.H获取配置().H函数集合.B绑定函数("latestVersion", 最新版本())
	api实例.H获取配置().H函数集合.B绑定函数("noteTags", 笔记标签())
	api实例.H获取配置().H函数集合.B绑定函数("bmTags", 书签标签())
	api实例.H获取配置().H函数集合.B绑定函数("cateBmNum", 分类书签数量())
	api实例.H获取配置().H函数集合.B绑定函数("fetchMetaBatch", 批量获取元数据())
}

var 获取URL信息 = config.H函数定义{
	C参数列表: []config.C参数项{
		{
			M名称: "url",
			L类型: "string",
			M描述: "URL地址",
		},
	},
	C处理函数: func(上下文 context.Context, 参数 model.FuncParam) (结果 any, 错误 error) {
		网址 := 参数["url"].String()

		var 元数据 *fetchurl.UrlMeta
		if !strings.HasPrefix(strings.ToLower(网址), "http") {
			元数据, 错误 = fetchurl.FetchURLMeta(上下文, "https://"+网址)
			if 错误 != nil {
				元数据, 错误 = fetchurl.FetchURLMeta(上下文, "http://"+网址)
			}
		} else {
			元数据, 错误 = fetchurl.FetchURLMeta(上下文, 网址)
		}

		if 元数据 != nil {
			if strings.HasPrefix(元数据.Icon, "/") {
				解析后的URL, _ := url2.Parse(元数据.Url)
				元数据.Icon = 解析后的URL.Scheme + "://" + 解析后的URL.Host + 元数据.Icon
			}

			元数据.Url = 网址
		}

		return 元数据, 错误
	},
}

var 导入书签 = config.H函数定义{
	C参数列表: []config.C参数项{
		{
			M名称: "data",
			L类型: "string",
		},
	},
	C处理函数: func(上下文 context.Context, 参数 model.FuncParam) (结果 any, 错误 error) {
		数据 := 参数["data"].String()

		书签列表, 错误 := htmlbookmark.Parse(上下文, 数据)
		if 错误 != nil {
			return nil, 错误
		}

		用户, _ := 上下文.Value(用户ID键).(*当前用户)

		错误 = g.DB().Transaction(上下文, func(上下文 context.Context, 事务 gdb.TX) error {
			记录, 错误 := g.DB().Ctx(上下文).Model("bookmark_cate").
				Where("group_id", 用户.用户ID).
				Where("parent_id", "root").
				Where("title", "导入").
				One()

			if 错误 != nil {
				return 错误
			}

			父级ID := ""
			if 记录.IsEmpty() {
				父级ID = strconv.FormatInt(idgen.NextId(), 10)
				_, 错误 := g.DB().Ctx(上下文).Model("bookmark_cate").Insert(g.Map{
					"cate_id":   父级ID,
					"title":     "导入",
					"sorts":     5,
					"parent_id": "root",
					"group_id":  用户.用户ID,
				})

				if 错误 != nil {
					return 错误
				}

			} else {
				父级ID = 记录.Map()["cate_id"].(string)
			}

			return 处理书签(上下文, 书签列表, 父级ID)
		})

		if 错误 != nil {
			return nil, 错误
		}

		return g.Map{}, nil
	},
}

func 处理书签(上下文 context.Context, 书签列表 *htmlbookmark.Bookmarks, 父级ID string) error {
	书签数据列表 := make([]model.Map, 0)
	书签分组列表 := make([]model.Map, 0)

	用户, _ := 上下文.Value(用户ID键).(*当前用户)

	for _, 项目 := range 书签列表.Children {
		if 项目.IsDir {
			分类ID := strconv.FormatInt(idgen.NextId(), 10)

			_, 错误 := g.DB().Ctx(上下文).Model("bookmark_cate").Insert(g.Map{
				"cate_id":   分类ID,
				"title":     项目.Title,
				"sorts":     5,
				"parent_id": 父级ID,
				"group_id":  用户.用户ID,
			})

			if 错误 != nil {
				return 错误
			}

			错误 = 处理书签(上下文, 项目, 分类ID)
			if 错误 != nil {
				return 错误
			}
		} else {
			书签ID := strconv.FormatInt(idgen.NextId(), 10)
			书签数据列表 = append(书签数据列表, g.Map{
				"bm_id":       书签ID,
				"title":       项目.Title,
				"url":         项目.URL,
				"description": "@import ",
			})

			书签分组列表 = append(书签分组列表, g.Map{
				"bm_id":    书签ID,
				"group_id": 用户.用户ID,
				"cate_id":  父级ID,
				"sorts":    5,
			})
		}
	}

	if len(书签数据列表) > 0 {
		_, 错误 := g.DB().Ctx(上下文).Model("bookmark").Insert(书签数据列表)

		if 错误 != nil {
			return 错误
		}

		_, 错误 = g.DB().Ctx(上下文).Model("group_bookmark").Insert(书签分组列表)

		if 错误 != nil {
			return 错误
		}
	}

	return nil
}

func 最新版本() config.H函数定义 {
	return config.H函数定义{
		C处理函数: func(ctx context.Context, param model.FuncParam) (res any, err error) {
			url := "https://api.github.com/repos/glennliao/bookmark/releases/latest"
			resp, err := gclient.New().Get(ctx, url)
			if err != nil {
				g.Log().Error(ctx, err)
				return "v0.0.0", nil
			}

			jsonObj := gjson.New(resp.ReadAllString())

			return jsonObj.Get("tag_name").String(), nil // 明确返回类型为string
		},
	}
}

func 笔记标签() config.H函数定义 {
	return config.H函数定义{
		C参数列表: nil,
		P批量处理: false,
		C处理函数: func(ctx context.Context, param model.FuncParam) (res any, err error) {
			user, _ := ctx.Value(用户ID键).(*当前用户)

			type 标签记录 struct {
				标签 []string
			}

			var 列表 []标签记录
			var 标签集 gset.StrSet

			err = g.DB().Model("note").Where("group_id", user.用户ID).Scan(&列表)
			if err != nil {
				return nil, err
			}

			if len(列表) == 0 {
				return []string{}, nil
			}

			for _, 记录 := range 列表 {
				for _, 标签 := range 记录.标签 {
					标签集.Add(标签)
				}
			}

			return 标签集.Slice(), err
		},
	}
}

func 书签标签() config.H函数定义 {
	return config.H函数定义{
		C参数列表: nil,
		P批量处理: false,
		C处理函数: func(ctx context.Context, param model.FuncParam) (res any, err error) {
			user, _ := ctx.Value(用户ID键).(*当前用户)

			type 标签记录 struct {
				标签 []string
			}

			var 列表 []标签记录
			var 标签集 gset.StrSet

			err = g.DB().Model("bookmark").Where("group_id", user.用户ID).Scan(&列表)
			if err != nil {
				return nil, err
			}

			if len(列表) == 0 {
				return []string{}, nil
			}

			for _, 记录 := range 列表 {
				for _, 标签 := range 记录.标签 {
					标签集.Add(标签)
				}
			}

			return 标签集.Slice(), err
		},
	}
}

func 分类书签数量() config.H函数定义 {
	return config.H函数定义{
		C参数列表: nil,
		P批量处理: false,
		C处理函数: func(ctx context.Context, param model.FuncParam) (res any, err error) {
			groupId := ""
			if param["groupId"] == nil {
				user, _ := ctx.Value(用户ID键).(*当前用户)
				groupId = user.用户ID
			} else {
				groupId = gconv.String(param["groupId"])
			}
			结果, err := g.DB().Model("bookmark").Where("group_id", groupId).WhereNull("drop_at").Group("cate_id").Fields("cate_id as cateId, count(1) as cnt").All()
			return 结果.List(), err
		},
	}
}

func 批量获取元数据() config.H函数定义 {
	return config.H函数定义{
		C处理函数: func(ctx context.Context, param model.FuncParam) (res any, err error) {
			所有, err := g.DB().Ctx(ctx).Model("bookmark").Where("description", gstr.Contains, "@import ").All()
			if err != nil {
				return nil, err
			}

			任务 := func(数据 interface{}) {
				ctx := gctx.NeverDone(ctx)
				项 := 数据.(g.Map)
				url := gconv.String(项["url"])
				bmId := gconv.String(项["bm_id"])
				var 元数据 *fetchurl.UrlMeta // 假设fetchurl包已经导入
				if !strings.HasPrefix(strings.ToLower(url), "http") {
					元数据, err = fetchurl.FetchURLMeta(ctx, "https://"+url)
					if err != nil {
						元数据, err = fetchurl.FetchURLMeta(ctx, "http://"+url)
						if err != nil {
							g.Log().Error(ctx, err, url, bmId)
							return
						}
					}
				} else {
					元数据, err = fetchurl.FetchURLMeta(ctx, url)
					if err != nil {
						g.Log().Error(ctx, err, url, bmId)
						return
					}
				}

				if 元数据 != nil {
					if strings.HasPrefix(元数据.Icon, "/") {
						_url, err := url2.Parse(元数据.Url) // 假设url2包已经导入
						if err != nil {
							g.Log().Error(ctx, err, url, bmId)
							return
						}
						元数据.Icon = _url.Scheme + "://" + _url.Host + 元数据.Icon
					}

					元数据.Url = url
				}

				_, err = g.DB().Model("bookmark").Ctx(ctx).Where("bm_id", bmId).Update(g.Map{
					"icon":        元数据.Icon,
					"description": 元数据.Description,
				})
				if err != nil {
					g.Log().Error(ctx, err)
				}
			}

			池, _ := ants.NewPoolWithFunc(8, 任务)
			defer 池.Release()

			for _, 项 := range 所有 {
				err := 池.Invoke(项.Map())
				if err != nil {
					return nil, err
				}
			}
			return nil, err
		},
	}
}
